#include <io.h>

#include "../VenusGeometry/3d.h"
#include "Material.h"
#include "Triangle.h"
#include "Mesh.h"
#include "TriDSLoader.h"

namespace Venus
{
	namespace Graphics
	{
		TriDSLoader::~TriDSLoader()
		{
			SAFE_RELEASE_A( m_Verts );
			SAFE_RELEASE_A( m_TCoords );
			SAFE_RELEASE_A( m_Faces );
			m_NrFaces = 0;
			m_NrVerts = 0;
			m_NrTCoords = 0;
		}

		bool TriDSLoader::Load( const char* a_File )
		{
			FILE* fd = fopen( a_File, "rb" );
			if ( NULL == fd ) {
				//Log when DEBUG

				return false;
			}

			uint16 chunkID;
			uint32 chunkLength;

			fseek( fd, 0, SEEK_END );
			long len = ftell( fd );
			fseek( fd, 0, SEEK_SET );

			while( ftell( fd ) < len )
			{
				fread( &chunkID, sizeof( chunkID ), 1, fd );
				fread( &chunkLength, sizeof( chunkLength ), 1, fd );
				switch( chunkID )
				{
					case MAIN3DS: 
						break;   
					
					case EDIT3DS:
						break;
					
					case EDIT_OBJECT:
						{
							int i = 0;
							char c;
							do
							{
								fread( &c, sizeof(char), 1, fd );
								_objName[i++] = c;	
							} while ( c != '\0' && i < 20 );
						}
						break;
					
					case OBJ_TRIMESH:
						break;
					
					case TRI_VERTEXL: 
						ReadVertexChunk( fd );
						break;
					
					case TRI_FACEL1:
						ReadFaceChunk( fd );
						break;
					
					case TRI_MAPPINGCOORS:
						ReadMappingCoordsChunk( fd );
						break;

					/************************************************************************
					 * materials and others should be added
					 ************************************************************************/
					case EDIT_MATERIAL:
						ReadMaterialChunk( fd );
						break;
					
					//----------- Skip unknow chunks ------------
					//We need to skip all the chunks that currently we don't use
					//We use the chunk lenght information to set the file pointer
					//to the same level next chunk
					//-------------------------------------------
					default:
						fseek( fd, chunkLength-6, SEEK_CUR );
				}
			}

			fclose( fd );
			return true;
		}

		Mesh* TriDSLoader::GetMesh( Material* a_Mat )
		{
			//if ( m_NrTCoords != m_NrVerts )
			//{
			//	// Log when DEBUG
			//	return NULL;
			//}

			Mesh* pMesh = new Mesh;
			pMesh->SetVertexCount( m_NrVerts );

			for ( size_t i = 0, j = 0, k = 0; i < m_NrVerts;
				++i, j += 3, k += 2 )
			{
				real x = m_Verts[ j ];
				real z = m_Verts[ j + 1 ];
				real y = m_Verts[ j + 2 ];
				if ( m_NrTCoords == m_NrVerts )
				{
					real u = m_TCoords[ k ];
					real v = m_TCoords[ k + 1 ];
					pMesh->AddVertex( Vertex( x, y, z, u, v ) );
				}
				else
				{
					pMesh->AddVertex( Vertex( x, y, z ) );
				}
			}

			if ( NULL != a_Mat )
			{
				pMesh->AddMaterial( a_Mat );
			}

			pMesh->SetTriangleCount( m_NrFaces );

			typedef std::list< uint16 > FacesShareVertex;
			FacesShareVertex* facesShareVert = new FacesShareVertex[ m_NrVerts ];
			for ( size_t i = 0, j = 0; i < m_NrFaces; ++i, j += 3 )
			{
				int aIdx = m_Faces[ j ];
				int bIdx = m_Faces[ j + 1 ];
				int cIdx = m_Faces[ j + 2 ];
				Triangle* pTriangle = pMesh->AddTriangle( aIdx, bIdx, cIdx );				

				if ( NULL != a_Mat )
					pTriangle->SetMaterial( a_Mat );

				facesShareVert[ aIdx ].push_back(i);
				facesShareVert[ bIdx ].push_back(i);
				facesShareVert[ bIdx ].push_back(i);
			}

			for ( uint16 i = 0; i < m_NrVerts; ++i )
			{
				Vector3 N = Vector3::ZERO;
				FacesShareVertex& FSV = facesShareVert[ i ];
				for( FacesShareVertex::iterator it = FSV.begin(); 
					FSV.end() != it; ++it )
				{
					N += pMesh->GetTriangle( *it )->GetNormal();
				}
				N /= FSV.size();
				pMesh->GetVertex( i )->SetNormal( N );
			}

			SAFE_RELEASE_A( facesShareVert );

			return pMesh;
		}

		void TriDSLoader::ReadVertexChunk( FILE* fd )
		{
			fread( &m_NrVerts, sizeof (m_NrVerts), 1, fd );
			m_Verts = new float[ m_NrVerts * 3 ];
			fread( m_Verts, sizeof( float ), m_NrVerts * 3, fd );
		}

		void TriDSLoader::ReadFaceChunk( FILE* fd )
		{
			fread( &m_NrFaces, sizeof (m_NrFaces), 1, fd );
			size_t faceIdxCount = m_NrFaces * 3;
			m_Faces = new uint16[ faceIdxCount ];
			for ( size_t i = 0; i < faceIdxCount; i += 3 )
			{
				fread( &m_Faces[i], sizeof( uint16 ), 3, fd );
				uint16 flags;
				fread( &flags, sizeof (uint16), 1, fd );

			}
		}

		void TriDSLoader::ReadMappingCoordsChunk( FILE* fd )
		{
			fread( &m_NrTCoords, sizeof (uint16), 1, fd );
			m_TCoords = new float[ m_NrTCoords * 2 ];
			fread( m_TCoords, sizeof( float ), m_NrTCoords * 2, fd );
		}

		void TriDSLoader::ReadMaterialChunk( FILE* fd )
		{

		}

	}
}